/**
 * @brief 使用libuv实现的TCP客户端
 * @version 0.1
 * @date 2017-2-21
 * @author 宋炜
 */
#include <unistd.h>
#include <regex>
#include <thread>
#include <atomic>
#include <chrono>

#include "config.hpp"
#include "defines.hpp"
#include "err_code.h"
#include "tcpbase.hpp"
#include "uvlper.hpp"
#include "resolver.hpp"
#include "misc.h"
#include "timer.hpp"

extern void StartRebootTimer();

void do_tcp_on_alloc_buff(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf)
{
	CUVClient *obj = (CUVClient *)handle->data;
	if (obj == nullptr)
		return;

	buf->len = obj->m_i_buf.len;
	buf->base = obj->m_i_buf.base;
}

void do_on_recv(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf)
{
	CUVClient *pt = (CUVClient *)(stream->data);
	if (pt != NULL && nread >= 0){
		uv_mutex_lock(&(pt->m_mutex));
		if (pt->cb_recv)
			pt->cb_recv(nread, buf->base); //OnRecv( buf->base , nread , CUVClient::OK );

		uv_mutex_unlock(&(pt->m_mutex));
	}
	else if (nread < 0)	{
		std::cout << TBLUE << "\nSever Error.Remote server disconnect" << TNORMAL << std::endl;
		pt->is_connected = false;
		if (pt->cb_error)
			pt->cb_error(CUVClient::ERR_UVCLIENT_RECV);
	}
}

void on_tcp_connect(uv_connect_t *handle, int status)
{
	CUVClient *pt = (CUVClient *)(handle->data);
	pt->Error(status);
	pt->m_conn_flag = true;

	if (status == 0){ // 连接成功
		uv_read_start((uv_stream_t *)&(pt->m_client), do_tcp_on_alloc_buff, do_on_recv);
		pt->OnConnected( CUVClient :: OK );
	}
	else{ // 连接失败
		pt->OnConnected( CUVClient :: ERR_UVCLIENT_CONNECTION );
	}
}

void on_write_cb(uv_write_t *req, int status)
{
	CUVClient::st_wd *wd = (CUVClient::st_wd *)(req->data);
	CUVClient *obj = nullptr;
#if defined(__DEBUG__)
	std::cout << TYELLOW << "ON NET SEND DATA CALLED: status = " << TBLUE << status << std::endl;
#endif
	if (req != nullptr){
		if (wd == NULL)	return;
		uv_buf_t *pt = (uv_buf_t *)(wd->__buff);
		obj = (CUVClient *)(wd->__this);
		if (pt != nullptr && pt->base != NULL){
			free(pt->base);
			free(pt);
			size_t s;
			CUVClient::err_code e;
			if (status < 0)	{
				e = CUVClient::ERR_UVCLIENT_OPERATION;
			}else{
				e = CUVClient::OK;
			}
			s = wd->__ds;
			//回调入对象，处理对象内事务
			obj->OnSend(s, e);
		}
		free(wd);
		free(req);
	}
	if (status < 0)	{
		if (obj != nullptr){
			obj->Error(status);
			if (obj->cb_error){
				obj->is_connected = false;
				obj->cb_error(CUVClient ::ERR_UVCLIENT_SEND);
			}
		}
	}
}

void libuv_shutdown_cb(uv_shutdown_t *req, int status)
{
	if (req) free(req);
}
/**
* @brief 连接服务器。
* @param arg ， 连接请求指针
*/
void ConnectThread4(CUVClient ::st_connreq *arg)
{
	if (arg == NULL) return;

	int iret;
	CUVClient *pclient = arg->obj;
	
	if (pclient == nullptr) return;
	
	iret = uv_tcp_connect(&(pclient->req), pclient->Client(), arg->add_link, on_tcp_connect );
	__show_line_1( "%s iret = %d%s" , TYELLOW , iret , TNORMAL );
	if (iret){
		__show_line_2();
		pclient->Error(iret);
		pclient->m_conn_flag = true;
		
		return;
	}
}

/**
* @brief 连接服务器。
* @param arg ， 连接对象指针
*/
void ConnectThread6(CUVClient ::st_connreq *arg)
{
	if (arg == NULL) return;

	CUVClient *pclient = arg->obj;

	if (pclient == nullptr) return;

	int iret = uv_tcp_connect(&(pclient->req), pclient->Client(), arg->add_link, on_tcp_connect);

	if (iret < 0){
		pclient->Error(iret);
		pclient->m_conn_flag = true;

		if (pclient->cb_error){
			pclient->cb_error(CUVClient::ERR_UVCLIENT_CONNECTION);
		}

		return;
	}
}
/**
 * @brief 响应uv_close操作的回调函数。
 */
void on_uv_close(uv_handle_t *h)
{
#if defined(__DEBUG__)
	std::cout << TYELLOW << "tcp is closed." << TNORMAL << std::endl;
#endif
	//CUVClient *pclient = (CUVClient*)(h->data);
	//pclient->OnClose();
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CUVClient ::CUVClient(const std::string &url, int port)
{
	is_connected 	= false;
	p_conn_current 	= nullptr;
	p_resolve_rst 	= nullptr;
	m_remote 		= url;
	m_port 			= port;

	m_o_buf = uv_buf_init((char *)malloc(1024), 1024);
	m_i_buf = uv_buf_init((char *)malloc(1024), 1024);
}

CUVClient ::~CUVClient()
{
	free_dns_result();
	stop();
}

void CUVClient ::stop()
{
	m_is_closed = false;
	if (is_connected == true){
		uv_read_stop((uv_stream_t *)&m_client);
		uv_shutdown_t *p_req = (uv_shutdown_t *)malloc(sizeof(uv_shutdown_t));
		if (p_req == NULL){
			std::cerr << TRED << "\nUV TCP Client can not alloc memory for uv_shutdown.\n" << TNORMAL << std::endl;
			abort();
		}
		uv_shutdown(p_req, (uv_stream_t *)&m_client, libuv_shutdown_cb);
	}

	uv_close((uv_handle_t *)&m_client, on_uv_close);

	bool flag = false;
	int i = 0;
	while (!flag){
		std::this_thread::yield();
		std::this_thread::sleep_for(std::chrono::milliseconds(100));
		flag = m_is_closed.load();
		i++;
		if (i >= 5000)        
			break;
	}
}

void CUVClient ::init()
{
	is_connected = false;
	int err = 0;
	looper *lp = GetLooper();
	if (lp)	{
		if (!err){
			err = uv_tcp_init(lp->get(), &m_client);

			if (!err){
				err = uv_mutex_init(&m_mutex);
				if (err){ //初始化互斥对象错误
					m_error = ERR_UVCLIENT_INIT_MUTEX;
					err_msg = "Initialize mutex fail";
					return;
				}
				//------------------------------------------
				m_client.data = this;
				req.data = this;
			}else{ //初始化TCP错误
				m_error = ERR_UVCLIENT_INIT_TCP;
				err_msg = "Initiallize tcp fail";
				return;
			}
		}else{ //执行 uv_loop_configure错误
			m_error = ERR_UVCLIENT_LOOP_CONFIG;
			err_msg = "Configure uv loop fail.";
			return;
		}
	}else{ //初始化事件循环错误
		m_error = ERR_UVCLIENT_LOOP_INIT;
		err_msg = "Initialize uv event loop fail.";
		return;
	}
}
void CUVClient ::Close()
{
	is_connected = false;
	uv_read_stop((uv_stream_t *)&m_client);
	uv_shutdown_t *req = (uv_shutdown_t *)malloc(sizeof(uv_shutdown_t));
	if (req == NULL){
		std::cerr << TRED << "\ncan not alloc memory." << TNORMAL << std::endl;
		exit(-1);
	}
	uv_shutdown(req, (uv_stream_t *)&m_client, libuv_shutdown_cb);
	if( cb_close ){
		cb_close();
	}
}

void CUVClient ::OnClose()
{
	m_is_closed = true;
}

void CUVClient ::OnSend(size_t s, CUVClient::err_code e)
{
	if (cb_send && e == OK)
		cb_send(s);
	else if (cb_error && e != OK){
		is_connected = false;
		cb_error(e);
	}
}

void CUVClient ::connect4( struct st_connreq *req )
{
	m_conn_flag = true;
	int iret = uv_thread_create(&threadhanlde, (void (*)(void *))ConnectThread4, req);
	if (iret){

		if (cb_error){
			is_connected = false;
			cb_error(UV_CLIENT_CREATE_THD);
			m_conn_overtime.Stop();
		}
		Error(iret);

		return;
	}
}

void CUVClient ::connect6()
{
	m_conn_flag = true;
	struct st_connreq *req = (struct st_connreq *)malloc(sizeof(struct st_connreq));
	struct sockaddr_in6 *addr = (struct sockaddr_in6 *)malloc(sizeof(struct sockaddr_in6));
	uv_ip6_addr(m_remote.c_str(), m_port, addr);
	req->obj = this;
	req->add_link = (sockaddr *)addr;
	int iret = uv_thread_create(&threadhanlde, (void (*)(void *))ConnectThread6, req);
	if (iret){
		free(addr);
		free(req);
		Error(iret);

		if (cb_error){
			is_connected = false;
			cb_error(UV_CLIENT_CREATE_THD);
		}
		return;
	}
}

void CUVClient :: save_dns_result( struct addrinfo *rest )
{
	if( rest ){

		struct addrinfo * p1 = rest , * p2 = p_resolve_rst;
		do{// 提取数据
			p2 = ( struct addrinfo * )malloc( sizeof( struct addrinfo ));
			memset( p2 , 0 , sizeof( struct addrinfo ));
			if( p_resolve_rst == nullptr ){
				p_resolve_rst = p2;
			}
			p2 -> ai_flags 		= p1 -> ai_flags;
			p2 -> ai_family 	= p1 -> ai_family; 
			p2 -> ai_socktype 	= p1 -> ai_socktype;
			p2 -> ai_protocol 	= p1 -> ai_protocol;
			p2 -> ai_addrlen 	= p1 -> ai_addrlen;
			p2 -> ai_addr 		= ( struct sockaddr *)malloc( sizeof( struct sockaddr ));

			if( p2 -> ai_addr ){
				memcpy( p2 -> ai_addr , p1->ai_addr , p1 -> ai_addrlen );
				if( p1->ai_canonname ){
					size_t len = strlen( p1 -> ai_canonname );
					p2 -> ai_canonname = ( char *)malloc( len );
					if( p2 -> ai_canonname ){
						strcpy( p2 -> ai_canonname , p1-> ai_canonname );
						p2 -> ai_next = p1 -> ai_next;
					}else{
						free( p2 );
						p2 = nullptr;
						p_resolve_rst = nullptr;
						throw ERR_ALLOC_MEM;
					}
				}
			}else{
				free( p2 );
				p2 = nullptr;
				p_resolve_rst = nullptr;
				throw ERR_ALLOC_MEM;
			}
			// 链检查
			p1 = p1->ai_next;
		}while( p1 != nullptr );
	}
}

void CUVClient :: free_dns_result()
{
	p_conn_current = nullptr;
	struct addrinfo * p = p_resolve_rst , *p1 = nullptr;

	if( p != nullptr ){
		do{
			p1 = p->ai_next;

			if( p->ai_addr ) free( p->ai_addr );
			if( p->ai_canonname ) free( p->ai_canonname );

			free( p );
			p = p1;
		}while( p != nullptr );
	}
	p_resolve_rst = nullptr;
}

void CUVClient ::init_dns_result()
{
	p_conn_current = nullptr;
	struct addrinfo * p = p_resolve_rst , *p1 = nullptr;

	if( p != nullptr ){
		do{
			p1 = p->ai_next;

			if( p->ai_addr ) free( p->ai_addr );
			if( p->ai_canonname ) free( p->ai_canonname );

			free( p );
			p = p1;
		}while( p != nullptr );

		p_resolve_rst = nullptr;
	}
}

void CUVClient ::connecturl()
{
	is_connected 	= false;
	m_is_closed 	= false;
	
	init_dns_result();
	__show_line_2();
	try{
		resolver *prs = nullptr;
		prs = new resolver (m_remote, m_port, true, [this , prs ](struct addrinfo *rest) {
            		if( rest != nullptr ){
				struct sockaddr * p = nullptr;
				// 保存DNS解析结果
				try{
					save_dns_result( rest );
					p_conn_current = rest;
                			p = rest->ai_addr;
					if( p != nullptr )  real_connect( p );
						__show_line_2();
				}catch( err_code e ){
					__show_line_1( "%s%s%s" , TRED , "Allocate memory fail." , TNORMAL );
					return;
				}
            		}//if( rest != nullptr 
			__show_line_2();
			delete prs;
			__show_line_2();
		}, // end of on success function
		[ this , prs ](int err) {
			__show_line_2();
			if (err == 0) return;
			if (cb_error){ cb_error(ERR_UVCLIENT_DNS); }
			is_connected = false;
			std::cerr << uv_strerror(err) << std::endl;

			delete prs;
		});
	}catch( std::bad_alloc& e ){
		__show_line_1( "%s%s%s" , TRED , "Allocate memory fail." , TNORMAL );
		return;
	}

}

CUVClient ::err_code CUVClient ::Send(const char *data, size_t s)
{
	__show_line_1( "%s%s%s" , TYELLOW , __PRETTY_FUNCTION__ , TNORMAL );
	if( data == nullptr ) return ERR_DATA_NULL;
	if( s == 0 ) return ERR_SEND_DATA_EMPTY;
	int ret = 0;
	if (is_connected == false){
		return ERR_UVCLIENT_CONNECTION;
	}
	uv_write_t *req_write; //发送请求句柄
	req_write = (uv_write_t *)malloc(sizeof(uv_write_t));
	if (req_write == nullptr){
		if (cb_error){
			cb_error(ERR_ALLOC_MEM);
		}
	}
	uv_buf_t *b = (uv_buf_t *)malloc(sizeof(uv_buf_t));
	if (b == nullptr){
		is_connected = false;
		if (cb_error){
			cb_error(ERR_UVCLIENT_SEND);
		}
	}
	*b = uv_buf_init((char *)malloc(s), s);
	//初始化需要传递给发送完成回调函数的参数.
	//分配的的内存在两个地方释放，1  如果正常完成操作，在回调函数中释放；2 如果没有发送完成
	//在错误处理中释放
	struct st_wd *wd = (struct st_wd *)malloc(sizeof(struct st_wd));
	if (wd == NULL)
		throw ERR_ALLOC_MEM; //内存分配失败

	wd->__buff 	= b;
	wd->__this 	= this;
	wd->__ds 	= s;
	//开始发送
	if (b->base != NULL){
		req_write->data = wd;
		memcpy(b->base, data, s);
		ret = uv_write(req_write, (uv_stream_t *)&m_client, b, 1, on_write_cb);
		
		if (ret < 0){
			is_connected = false;
			Error(ret);
			if (cb_error){
				cb_error(ERR_UVCLIENT_SEND);
			}
			free(wd);
			free(b->base);
		}
	}else{
		is_connected = false;
		free(wd);
		if (cb_error){
			cb_error(ERR_ALLOC_MEM);
		}
	}

	return OK;
}

void CUVClient ::connect()
{
	std::string url = m_remote;
	__show_line("connect( ) is called.");
	uv_tcp_keepalive(&m_client, true, 5);
	uv_tcp_nodelay(&m_client, 1);
	std::regex reg("\\d{1,3}(\\.\\d{1,2}){3}");
	if (url.find(':') != std::string::npos){ //如果字符串中包含了':',判断为IPV6
		connect6();
	}else{ //地址为域名
		__show_line("connect( ) is called.");
		
		connecturl();
	}
}

void CUVClient ::connect(const std::string &url, int port)
{
	m_remote 	= url;
	m_port 		= port;

	init();
	connect();
}

CUVClient ::err_code
CUVClient ::real_connect(struct sockaddr *add)
{
	struct st_connreq *req = (st_connreq *)malloc(sizeof(st_connreq));
	bool is_cancel = false;

	if (req){
		req->obj = this;
		req->add_link = (struct sockaddr *)malloc(sizeof(struct sockaddr));
		memcpy(req->add_link, add, sizeof(struct sockaddr));
		m_conn_flag = false; // start connect
		m_conn_overtime( 10000 );

		if ( req->add_link->sa_family == AF_INET ){
			__show_line("connect IPV4");
			ConnectThread4(req);
		}
		else if ( req->add_link->sa_family == AF_INET6 ){
			ConnectThread6(req);
		}
		
		m_conn_overtime.SetOwner([this, &is_cancel , req ] {
			__show_line_2();
			is_connected = false;
			uv_cancel((uv_req_t *)&req);

			if (cb_error){
				cb_error(CUVClient ::ERR_CONN_OVERTIME);
			}
			
			m_conn_flag = true;
		});

		m_conn_overtime.Start(-1, true);
		__show_line_2();
		free(req->add_link);
		free(req);		
		__show_line_2();	
	}else{ // alloc memory fail
		m_error = ERR_ALLOC_MEM;
		__show_line("Alloc memory fail");
		if (cb_error){
			cb_error(m_error);
		}
	}

	return OK;
}

void CUVClient ::OnConnected( err_code e )
{
	__show_line_2();
	m_conn_overtime.Stop();
	if( e == OK ){
		__show_line_2();
		is_connected = true;
		m_conn_flag = true;
		if (cb_conn) cb_conn();
		free_dns_result();
	}else{
		is_connected = false;
		__show_line_2();
		if( p_conn_current && p_conn_current -> ai_next ){
			__show_line_2();
			p_conn_current = p_conn_current->ai_next;
			real_connect( p_conn_current->ai_addr );
		}else{
			__show_line_2();
			free_dns_result();

			if (cb_error){
				__show_line_2();
				cb_error( ERR_UVCLIENT_CONNECTION );
			}
		}
	}
}
